import { deepClone } from './ObjectUtil';
type IGenerateProps = (value: any) => void;

type IArrayType = Array<string | number | boolean>;
type IItemType = string | number | boolean;

/**
 * 判断两个对象是否相等
 */
export const compare = (origin: any, target: any) => {
  if (typeof target !== 'object') {
    // target不是对象/数组
    // 直接返回全等的比较结果
    return origin === target;
  }

  if (typeof origin !== 'object') {
    // origin不是对象/数组
    // 直接返回false
    return false;
  }
  for (let key of Object.keys(target)) {
    // 遍历target的所有自身属性的key
    if (!compare(origin[key], target[key])) {
      // 递归比较key对应的value，
      // value不等，则两对象不等，结束循环，退出函数，返回false
      return false;
    }
  }
  // 遍历结束，所有value都深度比较相等，则两对象相等
  return true;
};

/**
 * 判断一个元素是否存在数组中
 * @param arr
 * @param item
 * @returns
 */
export const inArray = (arr: IArrayType, item: IItemType) => {
  return arr.indexOf(item) > -1;
};

/**
 * 深度判断一个元素是否存在于一个集合中
 */
export const isContain = (arr: IArrayType, item: IItemType | object) => {
  let flag = false;
  for (let i = 0; i < arr.length; i++) {
    if (typeof item === 'object') {
      if (compare(arr[i], item)) {
        flag = true;
        break;
      }
    } else {
      if (arr[i] === item) {
        flag = true;
        break;
      }
    }
  }
  return flag;
};

/**
 * 集合去重
 * Returns a unique array返回一个独特的阵列
 * @method unique
 * @param {Array Like} arr 集合数据
 * @return {Array LIke} 去重后的结果
 */
export const unique = (arr: IArrayType = []) => {
  if (new Set()) {
    return Array.from(new Set(arr));
  }

  let result: IArrayType = [];
  for (var i = 0; i < arr.length; i++) {
    const item = arr[i];
    if (!isContain(result, item)) {
      result.push(item);
    }
  }
  return result;
};

/**
 * 求两个集合的并集(a+b)
 *
 * @method union
 * @param {Array Like} a 集合a
 * @param {Array Like} b 集合b
 * @return {Array Like} 返回并集
 */
export const union = (a: IArrayType, b: IArrayType) => {
  return unique(a.concat(b));
};

/**
 * 求两个集合的交集
 *
 * @method intersection
 * @param {Array Like} a 集合a
 * @param {Array Like} b 集合b
 * @return {Array Like} 返回交集
 */
export const intersection = (a: IArrayType, b: IArrayType) => {
  a = unique(a);
  b = unique(b);
  let result: IArrayType = [];
  a.forEach((item) => {
    if (isContain(b, item)) {
      result.push(item);
    }
  });

  return result;
};

/**
 * 求两个集合的差集(a-b)
 *
 * @method difference
 * @param {Array Like} a 集合a
 * @param {Array Like} b 集合b
 * @return {Array Like} 返回差集
 */
export const difference = (a: IArrayType = [], b: IArrayType = []) => {
  a = unique(a);
  b = unique(b);
  const result: IArrayType = [];
  a.forEach((item) => {
    if (!isContain(b, item)) {
      result.push(item);
    }
  });

  return result;
};

/**
 * 删除数组对象
 * @param arr
 * @param index
 * @param num
 * @returns
 */
export const deleteArrayItem = (
  arr: IArrayType = [],
  index: number,
  num: number = 1,
) => {
  const len = arr.length;
  if (index > len || num < 0) {
    return [];
  }
  return arr.splice(index, num);
};

/**
 * 数组分组|分组统计
 * @param data
 * @param generateKey 参数归一化：可传具体某个对象key，也可以传函数
 * @param count
 * @returns
 */
export function groupBy<T>(
  data: T[],
  generateKey: string | keyof T | IGenerateProps,
  isCount?: boolean,
) {
  if (typeof generateKey === 'string') {
    const propName = generateKey;
    generateKey = (item) => item[propName];
  }

  const result: any = {};
  for (const item of data) {
    const key: any = (generateKey as IGenerateProps)(item);

    if (!result[key]) {
      result[key] = isCount ? 0 : [];
    }

    if (isCount) {
      result[key]++;
    } else {
      result[key].push(item);
    }
  }

  return result;
}

export function groupBy2(
  data: any[],
  generateKey: IGenerateProps,
  isCount?: boolean,
) {
  if (!Array.isArray(data) || !data.length) {
    throw new Error('The input array is invalid or empty.');
  }

  if (typeof generateKey !== 'function' && typeof generateKey !== 'string') {
    throw new Error(
      'The generateKey parameter should be a function or a string.',
    );
  }

  const result: any = {};
  const keyCache: any = {};

  for (const item of data) {
    let key;
    if (typeof generateKey === 'string') {
      if (!item.hasOwnProperty(generateKey)) {
        throw new Error(
          'The property ' +
            generateKey +
            ' does not exist in one or more items.',
        );
      }
      key = item[generateKey];
    } else {
      key = keyCache[item] || (keyCache[item] = generateKey(item));
    }

    if (!result[key]) {
      result[key] = isCount ? 0 : [];
    }
    if (isCount) {
      result[key]++;
    } else {
      result[key].push(item);
    }
  }

  return result;
}

/**
 * 二维数组对象根据指定key排序
 * @param data 要排序的数组
 * @param key 需要排序的键名
 * @param desc 是否倒序
 * @returns
 */
export function sortByKeys<T = any>(
  data: T[] = [],
  key: keyof T,
  desc?: boolean,
): T[] {
  // 深拷贝一份
  let dataTemp = deepClone(data);

  return dataTemp.sort(function (a: T, b: T) {
    if (!desc) {
      // 如果值是undefined,则自动倒序
      if (a[key] === undefined) {
        return -1;
      }
      return a[key] && (a[key] > b[key] ? 1 : a[key] < b[key] ? -1 : 0);
    } else {
      // 如果值是undefined,则自动倒序
      if (b[key] === undefined) {
        return -1;
      }
      return b[key] && (b[key] > a[key] ? 1 : b[key] < a[key] ? -1 : 0);
    }
  });
}

/**
 * 二维数组对象根据指定key排序
 * @param data 要排序的数组
 * @param key 需要排序的键名
 * @param desc 是否倒序
 * @returns
 */
export function sortByKey<T = any>(
  data: T[],
  key: keyof T,
  desc?: boolean,
): T[] {
  // 深拷贝一份
  let dataTemp = deepClone(data);

  dataTemp = dataTemp.sort(function (a: T, b: T) {
    // 如果值是undefined,则自动倒序
    if (a[key] === undefined || b[key] === undefined) {
      return -1;
    }

    // 判断是不是数字,数字则不处理,不是数字则转为对应Unicode码
    const targetA =
      a[key] !== null && !isNaN(Number(a[key])) ? a[key] : a[key] + '';
    const targetB =
      b[key] !== null && !isNaN(Number(b[key])) ? b[key] : b[key] + '';

    if (desc) {
      return targetA <= targetB ? 1 : -1;
    } else {
      return targetB <= targetA ? 1 : -1;
    }
  });

  return dataTemp;
}

/**
 * items 原始数据
 * idKey id字段名
 * parentIdKey parentId字段名
 */
export function buildTreeOptimize(
  items: any,
  idKey: string,
  parentIdKey: string,
) {
  // 把每个子节点保存起来，以便后面插入父节点
  const map = new Map();

  let i = 0,
    j = 0,
    len = items.length;
  // 对每节点循环，找其父节点，并且放到数组中
  for (; i < len; i++) {
    const item = items[i];
    const key = item[parentIdKey];

    // map 中有父数据，插入，没有，构建并插入
    map.has(key) ? map.get(key).push(item) : map.set(key, [item]);
  }

  // 树第一层
  const treeRoots = [];

  // 对每一个节点循环，找其子节点
  for (; j < len; j++) {
    const item = items[j];
    const key = item[parentIdKey];

    // 子节点插入当前节点
    item.children = map.get(item[idKey]) || [];
    // 当前节点不具备父节点，插入第一层数组中
    if (!key) {
      treeRoots.push(item);
    }
  }

  // 返回树结构
  return treeRoots;
}
